package com.google.code.mergeinformer.service.impl;

import com.google.code.mergeinformer.model.security.RememberMeToken;
import com.google.code.mergeinformer.model.security.User;
import com.google.code.mergeinformer.service.UserService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.web.authentication.rememberme.PersistentRememberMeToken;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.PersistenceContext;
import java.util.Date;

/**
 * @author Dmitry Golubev
 */
@Service
public class RememberMeServiceImpl implements PersistentTokenRepository {

    private static final Logger logger = LoggerFactory.getLogger(RememberMeServiceImpl.class);

    @PersistenceContext
    private EntityManager em;

    @Autowired
    private UserService userService;

    @Override
    @Transactional
    public void createNewToken(PersistentRememberMeToken token) {
        final RememberMeToken rememberMeToken = convert(token);
        em.persist(rememberMeToken);
    }

    @Override
    @Transactional
    public void updateToken(String series, String tokenValue, Date lastUsed) {
        final RememberMeToken token = findBySeries(series);
        if (token == null) {
            logger.error("Can not find remember-me token with series " + series);
            return;
        }
        token.setToken(tokenValue);
        token.setLastUsed(lastUsed);
    }

    @Override
    public PersistentRememberMeToken getTokenForSeries(String seriesId) {
        final RememberMeToken rememberMeToken = findBySeries(seriesId);
        if (rememberMeToken == null) {
            logger.error("Can not find remember-me token with series " + seriesId);
            return null;
        }
        return convert(rememberMeToken);
    }

    @Override
    @Transactional
    public void removeUserTokens(String username) {
        em.createNamedQuery("RememberMeToken.removeUserTokens").setParameter("username", username).executeUpdate();
    }

    private RememberMeToken convert(PersistentRememberMeToken token) {
        final RememberMeToken converted = new RememberMeToken();

        converted.setSeries(token.getSeries());
        converted.setToken(token.getTokenValue());
        converted.setLastUsed(token.getDate());
        final User user = userService.findUser(token.getUsername());
        user.addRememberMeToken(converted);

        return converted;
    }

    private PersistentRememberMeToken convert(RememberMeToken token) {
        return new PersistentRememberMeToken(token.getUser().getUsername(), token.getSeries(), token.getToken(), token.getLastUsed());
    }

    private RememberMeToken findBySeries(String seriesId) {
        try {
            return em.createNamedQuery("RememberMeToken.findBySeries", RememberMeToken.class)
                    .setParameter("series", seriesId).getSingleResult();
        } catch (NoResultException e) {
            return null;
        }
    }
}
